﻿using System;
using System.Collections.Generic;
using System.Net.Mail;
using System.Text;
using Foothill.Common;
using Foothill.Common.Logging;
using Foothill.DataAccess.Interface;
using Foothill.Model;
using Foothill.Service.Email;
using Foothill.Service.SystemMessage;
using Foothill.Service.Validation;
using Foothill.DataAccess;
using Foothill.Service.Exceptions;
using System.Web;
using System.IO;
using Foothill.Service.Configuration;
using System.Configuration;

namespace Foothill.Service.UserAccount
{
    public class UserAccountService : IUserAccountService
    {
        private IUserAccountRepo _userAccountRepo;
        private ISystemMessageService _systemMessageService;
        private IStringGenerator _stringGenerator;
        private IEmailSender _emailSender;
        private IValidationService _validationService;
        private IHashUtility _hashUtility;
        private readonly IHostSettingManager _hostSettingManager;

        public UserAccountService(IUserAccountRepo userAccountRepo, ISystemMessageService systemMessageService, IStringGenerator stringGenerator,
            IEmailSender emailSender, IValidationService validationService, IHashUtility hashUtility, IHostSettingManager hostSettingManager)
        {
            _userAccountRepo = userAccountRepo;
            _systemMessageService = systemMessageService;
            _stringGenerator = stringGenerator;
            _emailSender = emailSender;
            _validationService = validationService;
            _hashUtility = hashUtility;
            _hostSettingManager = hostSettingManager;
        }
        
        public UserAccountDto GetUserAccountDetailsByUserName(string username)
        {
            if (string.IsNullOrWhiteSpace(username))
            {
                return null;
            }

            return _userAccountRepo.GetUserAccountDetailsByUserName(username);
        }


        public UserAccountDto GetUserAccountDetailsByUserId(int userId)
        {
            return _userAccountRepo.GetUserAccountDetailsByUserId(userId);
        }

        public void SavePost(int userId, int postId)
        {
            try
            {
                _userAccountRepo.SavePost(userId, postId);
            }
            catch (DataAccessException e)
            {
                throw new FailToSavePostException("Fail to save post", e);
            }
        }

        public bool RecoverPassword(string email, out string feedback)
        {
            feedback = string.Empty;
            UserAccountDto userAccount = _userAccountRepo.GetUserAccountByEmail(email);

            if (userAccount == null)
            {
                feedback = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, SecurityMessageCode.PasswordRecovery_CannotFindEmail);
                return false;
            }
            else
            {
                // get temp password
                string tmpPassword = _stringGenerator.GenerateTempPassword();

                // send temp password via email
                try
                {
                    SendPasswordRecoveryEmail(email, tmpPassword);
                }
                catch (Exception ex)
                {
                    Log.Error(FoothillModule.Service, "Error in sending password recovery email", ex);
                    feedback = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, SecurityMessageCode.PasswordRecovery_FailToSendEmail);

                    return false;
                }

                try
                {
                    _userAccountRepo.UpdateUserAccountWithTempPassword(userAccount.UserAccountId, _hashUtility.HashText(tmpPassword), (int)UserAccountState.UsingTempPassword);
                }
                catch (Exception ex)
                {
                    Log.Error(FoothillModule.Service, "Error in updating user account with temp password", ex);
                    feedback = "Unexpected error";

                    return false;
                }

                feedback = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, SecurityMessageCode.PasswordRecovery_Succeed);
                return true;
            }
        }

        public bool ChangePassword(CurrentLoginUser loginUser, string currentPassword, string newPassword, 
            string confirmPassword, IList<ChangePasswordFields> fieldsToHighlight, out string errorMessage)
        {
            if (!CheckChangePasswordConditions(loginUser.UserAccountInfo.Email, currentPassword, newPassword, confirmPassword, fieldsToHighlight, out errorMessage))
            {
                return false;
            }

            // change password in db
            try
            {
                _userAccountRepo.ChangePassword(loginUser.UserAccountInfo.UserAccountId, _hashUtility.HashText(newPassword));
            }
            catch
            {
                errorMessage = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, SecurityMessageCode.ChangePassword_FailToUpdatePasswordInDb);
                return false;
            }
            
            return true;
        }

        public bool ChangePasswordAfterLoginUsingTempPassword(CurrentLoginUser loginUser, string newPassword, string confirmPassword, out string errorMessage)
        {
            if (!CheckChangeTempPasswordConditions(newPassword, confirmPassword, out errorMessage))
            {
                return false;
            }

            try
            {
                _userAccountRepo.ChangePasswordAfterLoginUsingTempPassword(loginUser.UserAccountInfo.UserAccountId, (int)UserAccountState.Active, _hashUtility.HashText(newPassword));
            }
            catch
            {
                errorMessage = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, SecurityMessageCode.ChangePassword_FailToUpdatePasswordInDb);
                return false;
            }

            return true;
        }

        public bool UpdateUserAccount(int userId, UserAccountDto dto)
        {
            try
            {
                _userAccountRepo.UpdateUserAccount(userId, dto);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public string GetLogoFilePath(int userId, string uploadedPhotoName)
        {
            string path = Path.Combine(GlobalSettings.UserLogoFolder, GetUserLogoFileName(userId, uploadedPhotoName));
            return HttpContext.Current.Server.MapPath(path);
        }

        public string GetProfilePhotoFilePath(int userId, string uploadedPhotoName)
        {
            string path = Path.Combine(GlobalSettings.UserProfilePhotoFolder, GetUserLogoFileName(userId, uploadedPhotoName));
            return HttpContext.Current.Server.MapPath(path);
        }

        public bool UpdateLogoFilePath(int userId, string logoFilePath)
        {
            try
            {
                _userAccountRepo.UpdateLogoFilePath(userId, logoFilePath);
                return true;
            }
            catch 
            {
                return false;
            }
        }

        public bool UpdateProfilePhotoFilePath(int userId, string filePath)
        {
            try
            {
                _userAccountRepo.UpdateProfilePhotoFilePath(userId, filePath);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public string GetLogoFileUrl(string filePath)
        {
            return Path.Combine(GlobalSettings.UserLogoFolder, Path.GetFileName(filePath));
        }

        public string GetProfilePhotoFileUrl(string filePath)
        {
            return Path.Combine(GlobalSettings.UserProfilePhotoFolder, Path.GetFileName(filePath));
        }

        internal bool CheckChangeTempPasswordConditions(string newPassword, string confirmPassword, out string errorMessage)
        {
            errorMessage = string.Empty;
            PasswordFormatError passwordFormatError;
            
            // validate format of the new password
            if (!_validationService.ValidatePassword(newPassword, out passwordFormatError))
            {
                errorMessage = GetNewPasswordFormatErrorMessage(passwordFormatError);
                return false;
            }

            // make sure two passwords match
            if (!newPassword.Equals(confirmPassword))
            {
                errorMessage = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, SecurityMessageCode.ChangePassword_TwoPasswordNotmatch);
                return false;
            }

            return true;
        }

        internal bool CheckChangePasswordConditions(string email, string currentPassword, string newPassword, string confirmPassword, IList<ChangePasswordFields> fieldsToHighlight, out string errorMessage)
        {
            errorMessage = string.Empty;
            PasswordFormatError passwordFormatError;

            // validate current password format 
            if (!_validationService.ValidatePassword(currentPassword, out passwordFormatError))
            {
                fieldsToHighlight.Add(ChangePasswordFields.CurrentPassword);
                errorMessage = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, SecurityMessageCode.ChangePassword_InvalidCurrentPassword);
                return false;
            }

            // authenticate current password
            if (_userAccountRepo.GetUserAccountByEmailAndPassword(email, _hashUtility.HashText(currentPassword)) == null)
            {
                fieldsToHighlight.Add(ChangePasswordFields.CurrentPassword);
                errorMessage = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, SecurityMessageCode.ChangePassword_InvalidCurrentPassword);
                return false;
            }

            // validate format of the new password
            if (!_validationService.ValidatePassword(newPassword, out passwordFormatError))
            {
                fieldsToHighlight.Add(ChangePasswordFields.NewPassword);
                errorMessage = GetNewPasswordFormatErrorMessage(passwordFormatError);
                return false;
            }

            // make sure two passwords match
            if (!newPassword.Equals(confirmPassword))
            {
                fieldsToHighlight.Add(ChangePasswordFields.NewPassword);
                fieldsToHighlight.Add(ChangePasswordFields.ConfirmPassword);

                errorMessage = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, SecurityMessageCode.ChangePassword_TwoPasswordNotmatch);
                return false;
            }

            return true;
        }

        internal void SendPasswordRecoveryEmail(string email, string tmpPassword)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.AppendLine("您好");
            stringBuilder.AppendLine("您在温哥华二手车网的临时密码是:");
            stringBuilder.AppendLine();
            stringBuilder.AppendLine(tmpPassword);
            stringBuilder.AppendLine();
            stringBuilder.AppendLine("点击下面链接登录:");

            Uri url = new Uri(_hostSettingManager.GetSiteBaseUri(), "LoginUsingTempPassword/LoginUsingTempPassword.aspx");
            stringBuilder.AppendLine(url.ToString());

            using (MailMessage mailMessage = new MailMessage())
            {
                mailMessage.Subject = "温哥华二手车网 临时密码";
                mailMessage.Body = stringBuilder.ToString();
                mailMessage.IsBodyHtml = true;

                mailMessage.To.Add(new MailAddress(email));

                _emailSender.SendEmail(mailMessage);
            }
        }

        internal string GetUserLogoFileName(int userId, string uploadedPhotoName)
        {
            return string.Format("{0}{1}", userId, Path.GetExtension(uploadedPhotoName)); ;
        }

        private string GetNewPasswordFormatErrorMessage(PasswordFormatError passwordFormatError)
        {
            string errorMsg = string.Empty;

            string msgCode = null;
            switch (passwordFormatError)
            {
                case PasswordFormatError.IsEmpty:
                    msgCode = SecurityMessageCode.ChangePassword_NewPasswordIsEmpty;
                    break;
                case PasswordFormatError.TooLong:
                    msgCode = SecurityMessageCode.ChangePassword_NewPasswordTooLong;
                    break;
                case PasswordFormatError.TooShort:
                    msgCode = SecurityMessageCode.ChangePassword_NewPasswordTooShort;
                    break;
            }

            if (!string.IsNullOrEmpty(msgCode))
            {
                errorMsg = _systemMessageService.GetSystemMessage(SystemMessageCategory.Security, msgCode);
            }

            return errorMsg;
        }
    }
}
